package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.VendingMachineDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.*;
import com.lkd.exception.LogicException;
import com.lkd.http.controller.vo.CreateVMReq;
import com.lkd.service.*;
import com.lkd.utils.UUIDUtils;
import com.lkd.vo.Pager;
import com.lkd.vo.SkuVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
@Slf4j
public class VendingMachineServiceImpl extends ServiceImpl<VendingMachineDao, VendingMachineEntity> implements VendingMachineService {

/**
     * 查询设备
     * @param pageIndex
     * @param pageSize
     * @param status
     * @return
     */
    @Override
    public Pager<VendingMachineEntity> search(Long pageIndex, Long pageSize, Integer status, String innerCode) {
        Page<VendingMachineEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<VendingMachineEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(status != null,VendingMachineEntity::getVmStatus,status)
            .likeLeft(StringUtils.isNotBlank(innerCode),VendingMachineEntity::getInnerCode,innerCode);
        Page<VendingMachineEntity> machineEntityPage = this.page(page, queryWrapper);
        return Pager.build(machineEntityPage);
    }

/**
     * 根据编号查询售货机
     * @param innerCode
     * @return
     */
    @Override
    public VmVO findByInnerCode(String innerCode) {

        LambdaQueryWrapper<VendingMachineEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VendingMachineEntity::getInnerCode,innerCode);
        VendingMachineEntity vendingMachineEntity = getOne(queryWrapper);
        VmVO vmVO = new VmVO();
        if(vendingMachineEntity != null) {
            BeanUtils.copyProperties(vendingMachineEntity, vmVO);
            vmVO.setNodeAddr(vendingMachineEntity.getNode().getAddr());
            vmVO.setNodeName(vendingMachineEntity.getNode().getName());
        }
        return vmVO;
    }

    @Override
    public VendingMachineEntity findByClientId(String clientId) {
        QueryWrapper<VendingMachineEntity> qw = new QueryWrapper<>();
        qw.lambda().eq( VendingMachineEntity::getClientId ,clientId );
        return this.getOne(qw);
    }


    @Override
    public Pager<String> getAllInnerCodes(boolean isRunning, long pageIndex, long pageSize) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<VendingMachineEntity> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageIndex,pageSize);

        QueryWrapper<VendingMachineEntity> qw = new QueryWrapper<>();
        if(isRunning){
            qw.lambda()
                    .select(VendingMachineEntity::getInnerCode)
                    .eq(VendingMachineEntity::getVmStatus,1);
        }else {
            qw.lambda()
                    .select(VendingMachineEntity::getInnerCode)
                    .ne(VendingMachineEntity::getVmStatus,1);
        }
        this.page(page,qw);
        Pager<String> result = new Pager<>();
        result.setCurrentPageRecords(page.getRecords().stream().map(VendingMachineEntity::getInnerCode).collect(Collectors.toList()));
        result.setPageIndex(page.getCurrent());
        result.setPageSize(page.getSize());
        result.setTotalCount(page.getTotal());

        return result;
    }

    @Override
    public void updateOnLineStatus(String innerCode, Boolean status) {
//        Map statusMap = (Map) redisTemplate.boundHashOps(VMSystem.VM_STATUES_KEY).get(innerCode);
//        if(statusMap==null) {
//            statusMap=new HashMap();
//        }
//        statusMap.put("10001",status);//10001 断网断电   10002 货道   10003 传送轴
//        redisTemplate.boundHashOps(VMSystem.VM_STATUES_KEY).put(innerCode,statusMap);
    }

    @Autowired
    private NodeService nodeService;
    /**
     * 售货机新增
     * @param vmReq
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveVm(CreateVMReq vmReq) {

//        售货机类型id
        int vmType = vmReq.getVmType();
//         点位id
        Long nodeId = vmReq.getNodeId();
        NodeEntity nodeEntity = nodeService.getById(nodeId);
        if(nodeEntity == null){
            log.error("点位不存在");
            throw new LogicException("点位不存在");
        }

        VendingMachineEntity vendingMachineEntity = new VendingMachineEntity();
        vendingMachineEntity.setNodeId(nodeId);
        vendingMachineEntity.setBusinessId(nodeEntity.getBusinessId());
        vendingMachineEntity.setRegionId(nodeEntity.getRegionId());
        vendingMachineEntity.setOwnerId(nodeEntity.getOwnerId());
        vendingMachineEntity.setOwnerName(nodeEntity.getOwnerName());
        vendingMachineEntity.setVmType(vmType);
        vendingMachineEntity.setCreateUserId(vmReq.getCreateUserId());
//        售货机编号
        String innerCode = UUIDUtils.getUUID();
        vendingMachineEntity.setInnerCode(innerCode);
        vendingMachineEntity.setVmStatus(VMSystem.VM_STATUS_NODEPLOY);
        vendingMachineEntity.setClientId(UUIDUtils.generateClientId(innerCode));
//        保存售货机表
        save(vendingMachineEntity);
//        保存货道表
        saveChannel(vendingMachineEntity);
        return true;
    }

    /**
     * 修改售货机状态
     * @return
     */
    @Override
    public Boolean updateVmStatus(TaskCompleteContract contract) {

        LambdaUpdateWrapper<VendingMachineEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(VendingMachineEntity::getInnerCode,contract.getInnerCode());
        if(contract.getTaskType() == VMSystem.TASK_TYPE_DEPLOY){
//            投放工单,状态改为运营中
            updateWrapper.set(VendingMachineEntity::getVmStatus,VMSystem.VM_STATUS_RUNNING);
        }else{
            //            撤机工单,状态改为已撤机
            updateWrapper.set(VendingMachineEntity::getVmStatus,VMSystem.VM_STATUS_REVOKE);
        }

        return update(updateWrapper);

    }

    @Autowired
    private MqttProducer mqttProducer;
    @Override
    public void doSupply(int total,int index) {
//        查询售货机列表，只查询运营中的售货机
        LambdaQueryWrapper<VendingMachineEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(VendingMachineEntity::getVmStatus,VMSystem.VM_STATUS_RUNNING)
                .apply(" mod(id,"+total+")="+index);
        List<VendingMachineEntity> machineEntityList = list(queryWrapper);
        if(CollectionUtils.isEmpty(machineEntityList)){
            log.info("售货机没有数据");
            return ;
        }
        log.info("machineEntityList.size={}",machineEntityList.size());
//        查询货道
        for (VendingMachineEntity machineEntity : machineEntityList) {
            String innerCode = machineEntity.getInnerCode();
            List<ChannelEntity> channelEntityList = channelService.getChannelesByInnerCode(innerCode);
            if(CollectionUtils.isEmpty(channelEntityList)){
                log.info("货道没有数据");
                return ;
            }
//         找到skuid 不等0 and 当前库存<最大库存
            List<ChannelEntity> channels = channelEntityList.stream().filter(channelEntity -> {
                return channelEntity.getSkuId() != 0 && channelEntity.getCurrentCapacity() < channelEntity.getMaxCapacity();
            }).collect(Collectors.toList());
            if(CollectionUtils.isEmpty(channels)){
                log.info("货道没有数据");
                return ;
            }
//           构造发送消息的内容
            List<SupplyChannel> supplyChannelList = channels.stream().map(channelEntity -> {
                SupplyChannel supplyChannel = new SupplyChannel();
                BeanUtils.copyProperties(channelEntity, supplyChannel);
                supplyChannel.setCapacity(channelEntity.getMaxCapacity() - channelEntity.getCurrentCapacity());
                supplyChannel.setSkuName(channelEntity.getSku().getSkuName());
                supplyChannel.setSkuImage(channelEntity.getSku().getSkuImage());
                return supplyChannel;
            }).collect(Collectors.toList());
            if(CollectionUtils.isEmpty(supplyChannelList)){
                log.info("没有补货数据");
                return ;
            }
//        发送补货工单创建消息
            SupplyContract supplyContract = new SupplyContract();
            supplyContract.setInnerCode(innerCode);
            supplyContract.setSupplyData(supplyChannelList);
            try {
                mqttProducer.send(TopicConfig.TASK_SUPPLY_TOPIC,2,supplyContract);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            } catch (MqttException e) {
                e.printStackTrace();
            }


//            初始化商品的库存到 Redis
            for (VendingMachineEntity vendingMachineEntity : machineEntityList) {
                this.initSkuInventory(vendingMachineEntity);
            }

        }


    }

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private SkuService skuService;

    /**
     * 初始化 sku在redis中的库存
     * 如果已经存在就不要初始化
     * @param machineEntity
     */
    private void initSkuInventory(VendingMachineEntity machineEntity){
//        查询当前售货机可以售卖的sku的集合
        String innerCode = machineEntity.getInnerCode();
        List<SkuVO> skuVOList = skuService.getSkuListByInnerCode(innerCode);
        for (SkuVO skuVO : skuVOList) {
            String key = VMSystem.getSkuInventoryKey(innerCode, skuVO.getSkuId());
            Boolean hasKey = redisTemplate.hasKey(key);
            if(hasKey){
//                如果key存在，下个sku操作
                log.info("skuId={}，redis已经存在，下一个",skuVO.getSkuId());
                continue;
            }
            if(skuVO.getCapacity() >0){
//                商品有库存，就初始化
                redisTemplate.opsForValue().increment(key,skuVO.getCapacity());
            }
        }
    }

    @Autowired
    private ChannelService channelService;
    @Autowired
    private VmTypeService vmTypeService;
    /**
     * 创建售货机的货道数据
     * @param vendingMachineEntity
     */
    private void saveChannel(VendingMachineEntity vendingMachineEntity) {

        List<ChannelEntity> channelEntityList = new ArrayList<>();

        Long id = vendingMachineEntity.getId();
        String innerCode = vendingMachineEntity.getInnerCode();
        Integer vmTypeId = vendingMachineEntity.getVmType();
//        查询售货机的类型
        VmTypeEntity vmTypeEntity = vmTypeService.getById(vmTypeId);
        if(vmTypeEntity == null){
            log.error("售货机类型错误");
            throw new LogicException("售货机类型错误");
        }
        for(int i =1;i<= vmTypeEntity.getVmRow();i++) {
            for(int j=1;j<= vmTypeEntity.getVmCol();j++) {
                ChannelEntity channelEntity = new ChannelEntity();
                channelEntity.setChannelCode(i+"-"+j);
                channelEntity.setVmId(id);
                channelEntity.setInnerCode(innerCode);
                channelEntity.setMaxCapacity(vmTypeEntity.getChannelMaxCapacity());
                channelEntity.setCurrentCapacity(0);

                channelEntityList.add(channelEntity);
            }
        }
        channelService.saveBatch(channelEntityList);

    }
}
